package com.forg.mvc.core.plugin;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.forg.mvc.core.common.ClassUtil;

public class PluginManger {
    
    private static final Logger logger = LoggerFactory.getLogger(PluginManger.class);
    
    private static List<Class<?>> pluginClassList ;
    
    private static Map<String,Object> plugins = new HashMap<>() ;
    
    private static ExecutorService threadPool =  Executors.newFixedThreadPool(5);
    
    public static void init(){
        
        long startTime = System.currentTimeMillis();
        
        pluginClassList = ClassUtil.getAnnotationClassList(Plugin.class);
        
        if(CollectionUtils.isNotEmpty(pluginClassList)){
            
            List<Future<Boolean>> resultList = new ArrayList<>();
            for(Class<?> clazz : pluginClassList){
                resultList.add(threadPool.submit(new PluginInitThread(clazz)));
            }
            
            for(Future<Boolean> f:resultList){
                try {
                    f.get();
                } catch (Exception e) {
                    logger.error(e.getMessage());
                }
                
            }
        }
        
        logger.debug("PluginManger init cost {} ms",System.currentTimeMillis() - startTime);
    }
    
    public static boolean init(Class<?> clazz){
        try {
            
            String m = clazz.getAnnotation(Plugin.class).method();
            if(StringUtils.isBlank(m)){
                throw new RuntimeException(clazz.getName() + " has no init method.");
            }
            
            Method initMethod = clazz.getDeclaredMethod(m, new Class[0]);
            
            Object p = clazz.newInstance();
            
            initMethod.invoke(p, new Object[0]);
            
            String name  = clazz.getAnnotation(Plugin.class).name();
            if(StringUtils.isNotBlank(name)){
                plugins.put(name, p);
            }
        } catch (InvocationTargetException e) {
            throw new RuntimeException(clazz.getName() + " init error:" + e.getTargetException().getMessage(),e);
        }catch (Exception e) {
            throw new RuntimeException(clazz.getName() + " init error:" + e.getMessage(),e);
        }
        
        return true;
    }
    
    public static class PluginInitThread implements Callable<Boolean>{

        Class<?> clazz = null;
        public PluginInitThread(Class<?> clazz){
            this.clazz = clazz;
        }
        
        
        @Override
        public Boolean call() throws Exception {
            
            if(clazz == null) return Boolean.FALSE;
            
            return init(clazz);
        }
        
    }

}
